// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	12/16/2014 12:04:52 AM				
// -----------------------------

#include "TerrainTextureGenerator.h"

#include "Application.h"
#include "RenderManager.h"

#include "Noise1234.h"

#include "Filters.h"

ITexture2D* TerrainTextureGenerator::Generate(Application* lpApp, const TerrainTextureDesc& desc)
{
	SizeT dim = desc.Width * desc.Height;
	Float* lpData = new Float[dim * 4];
	memset(lpData, 0, dim * 4 * sizeof(Float));

	// Calculate the Initial Data
	for (UInt32 y = 0; y < desc.Height; ++y)
	{
		for (UInt32 x = 0; x < desc.Width; ++x)
		{
			UInt32 idx = (y * desc.Width) + x;
			Float heightVal = desc.HeightData[idx];
			Float offset = Math::RandomFloat(desc.MinOffset, desc.MaxOffset);
			heightVal += offset;
			heightVal = Math::Clamp(heightVal, 0.0f, 1.0f);

			// Calculate the Color
			Color clr = CalculateColor(desc, heightVal);			

			// Insert into the Texture
			idx *= 4;
			lpData[idx + 0] = clr.r;
			lpData[idx + 1] = clr.g;
			lpData[idx + 2] = clr.b;
			lpData[idx + 3] = clr.a;
		}
	}

	// Blur the Data
	lpData = Filters::Blur(desc.Width, desc.Height, 4, desc.BlurSamples, lpData);
	// Noise Variation
	lpData = Filters::NoiseVariateMultiply(desc.Width, desc.Height, 4, true, 0.4f, 1.0f, 5.0f, lpData);

	ITexture2D* lpTexture = ITexture2D::Create(lpApp->GetRenderer()->GetDevice(), desc.Width, desc.Height, Format::Float4, lpData);
	delete[] lpData;

	return lpTexture;
}

Color TerrainTextureGenerator::CalculateColor(const TerrainTextureDesc& desc, Float heightVal)
{
	TerrainTextureElementDesc prevElem = {};
	TerrainTextureElementDesc currElem = {};
	TerrainTextureElementDesc nextElem = {};

	Bool bHasPrev = false;
	Bool bHasNext = false;

	// Find the Elements
	for (UInt32 i = 0; i < desc.Elements.size(); ++i)
	{
		TerrainTextureElementDesc elem = desc.Elements[i];

		if (heightVal >= elem.Start)
		{
			if (i == 0 && desc.Elements.size() > 1) // First Element
			{
				currElem = elem;
				nextElem = desc.Elements[i + 1];
				bHasPrev = false;
				bHasNext = true;
			}
			else if (i == desc.Elements.size() - 1 && desc.Elements.size() > 1) // Last Element
			{
				prevElem = desc.Elements[i - 1];
				currElem = elem;				
				bHasPrev = true;
				bHasNext = false;
			}
			else if (desc.Elements.size() > 2)
			{
				prevElem = desc.Elements[i - 1];
				currElem = elem;
				nextElem = desc.Elements[i + 1];
				bHasPrev = true;
				bHasNext = true;
			}
			else if (desc.Elements.size() == 1)
			{
				currElem = elem;
				bHasPrev = false;
				bHasNext = false;
			}
		}
	}

	// Calculate the Color
	Color clr = currElem.Color;
	Float hb = desc.FadeBorder / 2.0f;

	if (bHasNext)
	{
		if (heightVal >= (nextElem.Start - hb))
		{
			Float a = heightVal - (nextElem.Start - hb);
			a /= desc.FadeBorder;

			clr = Color::Lerp(currElem.Color, nextElem.Color, a);
		}
	}
	if (bHasPrev)
	{
		Float h = heightVal - currElem.Start;
		if (h <= hb) // Lower Fade
		{
			Float a = h;
			a /= desc.FadeBorder;
			a += 0.5f;

			clr = Color::Lerp(prevElem.Color, currElem.Color, a);
		}
	}

	return clr;
}